#pragma once

namespace AVUI {

class UriParser;

/// <summary>Defines the parts of a URI for the <see cref="M:System.Uri.GetLeftPart(System.UriPartial)" /> method.</summary>
class UriPartial
{
public:
    enum Enum
    {
    /// <summary>The scheme segment of the URI.</summary>
    Scheme = 0,
    /// <summary>The scheme and authority segments of the URI.</summary>
    Authority = 1,
    /// <summary>The scheme, authority, and path segments of the URI.</summary>
    Path = 2,
    /// <summary>The scheme, authority, path, and query segments of the URI.</summary>
    Query = 3
    };
};

/// <summary>Defines the kinds of <see cref="T:System.Uri"></see>s for the <see cref="M:System.Uri.IsWellFormedUriString(System.String,System.UriKind)"></see> and several <see cref="Overload:System.Uri.#ctor"></see> methods.</summary>
class UriKind
{
public:
    enum Enum
    {
        /// <summary>The kind of the Uri is indeterminate.</summary>        
        RelativeOrAbsolute, 
        /// <summary>The Uri is an absolute Uri.</summary>
        Absolute = 1,
        /// <summary>The Uri is a relative Uri.</summary>
        Relative = 2 
    };
};

/// <summary>Defines host name types for the <see cref="M:System.Uri.CheckHostName(System.String)" /> method.</summary>
class UriHostNameType
{
public:
    enum Enum
    {
        /// <summary>The type of the host name is not supplied.</summary>
        Unknown = 0,
        /// <summary>The host is set, but the type cannot be determined.</summary>
        Basic = 1,
        /// <summary>The host name is a domain name system (DNS) style host name.</summary>
        Dns = 2,
        /// <summary>The host name is an Internet Protocol (IP) version 4 host address.</summary>
        IPv4 = 3,
        /// <summary>The host name is an Internet Protocol (IP) version 6 host address.</summary>
        IPv6 = 4
    };
};


/// <summary>Specifies the parts of a <see cref="T:System.Uri" />.</summary>
class UriComponents
{
public:
    enum Enum
    {
        AbsoluteUri = 0x7f,
        Fragment = 0x40,
        Host = 4,
        HostAndPort = 0x84,
        HttpRequestUrl = 0x3d,
        KeepDelimiter = 0x40000000,
        Path = 0x10,
        PathAndQuery = 0x30,
        Port = 8,
        Query = 0x20,
        Scheme = 1,
        SchemeAndServer = 13,
        SerializationInfoString = 0x80000000,
        StrongAuthority = 0x86,
        StrongPort = 0x80,
        UserInfo = 2
    };
};

// Internal syntax flags
class UriSyntaxFlags
{
public:
    enum Enum
    {
        AllowAnInternetHost = 0xe00,
        AllowAnyOtherHost = 0x1000,
        AllowDnsHost = 0x200,
        AllowDOSPath = 0x100000,
        AllowEmptyHost = 0x80,
        AllowIdn = 0x4000000,
        AllowIPv4Host = 0x400,
        AllowIPv6Host = 0x800,
        AllowIriParsing = 0x10000000,
        AllowUncHost = 0x100,
        BuiltInSyntax = 0x40000,
        CanonicalizeAsFilePath = 0x1000000,
        CompressPath = 0x800000,
        ConvertPathSlashes = 0x400000,
        FileLikeUri = 0x2000,
        MailToLikeUri = 0x4000,
        MayHaveFragment = 0x40,
        MayHavePath = 0x10,
        MayHavePort = 8,
        MayHaveQuery = 0x20,
        MayHaveUserInfo = 4,
        MustHaveAuthority = 1,
        OptionalAuthority = 2,
        ParserSchemeOnly = 0x80000,
        PathIsRooted = 0x200000,
        SimpleUserSyntax = 0x20000,
        UnEscapeDotsAndSlashes = 0x2000000,
        V1_UnknownUri = 0x10000
    };
};

/// <summary>Controls how URI information is escaped.</summary>
class UriFormat
{
public:
    enum Enum
    {
        /// <summary>Characters that have a reserved meaning in the requested URI components remain escaped. All others are not escaped.</summary>
        SafeUnescaped = 3,

        /// <summary>No escaping is performed.</summary>
        Unescaped = 2,

        /// <summary>Escaping is performed according to the rules in RFC 2396.</summary>
        UriEscaped = 1
    };
};


class UriFormatException : public Exception
{
public:
    UriFormatException(const char* szMessage) : Exception(szMessage) { };

    DECLARE_ELEMENT(UriFormatException, Exception);
};


/// <summary>Provides an object representation of a uniform resource identifier (URI) and easy access to the parts of the URI.</summary>
/// WPFG / Deviation, Uri implementation is very very light. Only used for relative versus absolute indexing. TBD - More complete version, for networked resources perhaps?
class Uri : public Object
{
public:
    DECLARE_ELEMENT(Uri, Object);

#pragma region Public Methods

    /// <summary>Initializes a new instance of the <see cref="T:System.Uri"></see> class with the specified URI.</summary>
    /// <param name="uriString">A URI. </param>
    /// <exception cref="T:System.ArgumentNullException">uriString is null. </exception>
    /// <exception cref="T:System.UriFormatException">uriString is empty.-or- The scheme specified in uriString is not correctly formed. See <see cref="M:System.Uri.CheckSchemeName(System.String)"></see>.-or- uriString contains too many slashes.-or- The password specified in uriString is not valid.-or- The host name specified in uriString is not valid.-or- The file name specified in uriString is not valid. -or- The user name specified in uriString is not valid.-or- The host or authority name specified in uriString cannot be terminated by backslashes.-or- The port number specified in uriString is not valid or cannot be parsed.-or- The length of uriString exceeds 65534 characters.-or- The length of the scheme specified in uriString exceeds 1023 characters.-or- There is an invalid character sequence in uriString.-or- The MS-DOS path specified in uriString must start with c:\\.</exception>
    Uri(String* pUriString);

    /// <summary>Initializes a new instance of the <see cref="T:System.Uri"></see> class with the specified URI. This constructor allows you to specify if the URI string is a relative URI, absolute URI, or is indeterminate.</summary>
    /// <param name="uriString">A string that identifies the resource to be represented by the <see cref="T:System.Uri"></see> instance.</param>
    /// <param name="uriKind">Specifies whether the URI string is a relative URI, absolute URI, or is indeterminate.</param>
    Uri(String* pUriString, UriKind::Enum uriKind);

    /// <summary>Initializes a new instance of the <see cref="T:System.Uri"></see> class based on the specified base URI and relative URI string.</summary>
    /// <param name="relativeUri">The relative URI to add to the base URI. </param>
    /// <param name="baseUri">The base URI. </param>
    Uri(Uri* pBaseUri, String* pRelativeUri);

    /// <summary>Initializes a new instance of the <see cref="T:System.Uri"></see> class based on the combination of a specified base <see cref="T:System.Uri"></see> instance and a relative <see cref="T:System.Uri"></see> instance.</summary>
    /// <param name="relativeUri">A relative <see cref="T:System.Uri"></see> instance that is combined with baseUri.</param>
    /// <param name="baseUri">An absolute <see cref="T:System.Uri"></see> that is the base for the new <see cref="T:System.Uri"></see> instance. </param>
    Uri(Uri* pBaseUri, Uri* pRelativeUri);

    /// <summary>Determines whether the specified host name is a valid DNS name.</summary>
    /// <returns>A <see cref="T:System.UriHostNameType" /> that indicates the type of the host name. If the type of the host name cannot be determined or if the host name is null or a zero-length string, this method returns <see cref="F:System.UriHostNameType.Unknown" />.</returns>
    /// <param name="schemeName">The host name to validate. </param>
    static UriHostNameType::Enum CheckHostName(String* pName);

    /// <summary>Determines whether the specified scheme name is valid.</summary>
    /// <returns>A <see cref="T:System.Boolean" /> value that is true if the scheme name is valid; otherwise, false.</returns>
    /// <param name="schemeName">The scheme name to validate. </param>
    static bool CheckSchemeName(String* pSchemeName);

    /// <summary>Compares the specified parts of two URIs using the specified comparison rules.</summary>
    /// <returns>An <see cref="T:System.Int32" /> value that indicates the lexical relationship between the compared <see cref="T:System.Uri" /> components.ValueMeaningLess than zero<paramref name="uri1" /> is less than <paramref name="uri2" />.Zero<paramref name="uri1" /> equals <paramref name="uri2" />.Greater than zero<paramref name="uri1" /> is greater than <paramref name="uri2" />.</returns>
    /// <param name="uri1">The first <see cref="T:System.Uri" />.</param>
    /// <param name="uri2">The second <see cref="T:System.Uri" />.</param>
    /// <param name="partsToCompare">A bitwise combination of the <see cref="T:System.UriComponents" /> values that specifies the parts of <paramref name="uri1" /> and <paramref name="uri2" /> to compare.</param>
    /// <param name="compareFormat">One of the <see cref="T:System.UriFormat" /> values that specifies the character escaping used when the URI components are compared.</param>
    /// <param name="comparisonType">One of the <see cref="T:System.StringComparison" /> values.</param>
    /// <exception cref="T:System.ArgumentException">
    static int Compare(Uri* pUri1, Uri* pUri2, UriComponents::Enum partsToCompare, UriFormat::Enum compareFormat, StringComparison::Enum comparisonType);

    /// <summary>Compares two <see cref="T:System.Uri" /> instances for equality.</summary>
    /// <returns>A <see cref="T:System.Boolean" /> value that is true if the two instances represent the same URI; otherwise, false.</returns>
    /// <param name="comparand">The <see cref="T:System.Uri" /> instance or a URI identifier to compare with the current instance. </param>
    bool Equals(const Object* pComparand) const AVUI_OVERRIDE;

    /// <summary>Converts a string to its escaped representation.</summary>
    /// <returns>A <see cref="T:System.String" /> that contains the escaped representation of <paramref name="stringToEscape" />.</returns>
    /// <param name="stringToEscape">The string to escape.</param>
    /// <exception cref="T:System.ArgumentNullException">
    /// <paramref name="stringToEscape" /> is null. </exception>
    /// <exception cref="T:System.UriFormatException">The length of <paramref name="stringToEscape" /> exceeds 32766 characters.</exception>
    static TRefCountedPtr<String> EscapeDataString(String* pStringToEscape);

    /// <summary>Converts a URI string to its escaped representation.</summary>
    /// <returns>A <see cref="T:System.String" /> that contains the escaped representation of <paramref name="stringToEscape" />.</returns>
    /// <param name="stringToEscape">The string to escape.</param>
    /// <exception cref="T:System.ArgumentNullException">
    /// <paramref name="stringToEscape" /> is null. </exception>
    /// <exception cref="T:System.UriFormatException">The length of <paramref name="stringToEscape" /> exceeds 32766 characters.</exception>
    static TRefCountedPtr<String> EscapeUriString(String* pStringToEscape);

    /// <summary>Gets the decimal value of a hexadecimal digit.</summary>
    /// <returns>An <see cref="T:System.Int32" /> value that contains a number from 0 to 15 that corresponds to the specified hexadecimal digit.</returns>
    /// <param name="digit">The hexadecimal digit (0-9, a-f, A-F) to convert. </param>
    /// <exception cref="T:System.ArgumentException">
    /// <paramref name="digit" /> is not a valid hexadecimal digit (0-9, a-f, A-F). </exception>
    static int FromHex(WCHAR digit);

    /// <summary>Gets the specified components of the current instance using the specified escaping for special characters.</summary>
    /// <returns>A <see cref="T:System.String" /> that contains the components.</returns>
    /// <param name="components">A bitwise combination of the <see cref="T:System.UriComponents" /> values that specifies which parts of the current instance to return to the caller.</param>
    /// <param name="format">One of the <see cref="T:System.UriFormat" /> values that controls how special characters are escaped. </param>
    /// <exception cref="T:System.ArgumentOutOfRangeException">
    /// <paramref name="uriComponents" /> is not a combination of valid <see cref="T:System.UriComponents" /> values.</exception>
    /// <exception cref="T:System.InvalidOperationException">The current <see cref="T:System.Uri" /> is not an absolute URI. Relative URIs cannot be used with this method.</exception>
    TRefCountedPtr<String> GetComponents(UriComponents::Enum components, UriFormat::Enum format);

    /// <summary>Gets the specified portion of a <see cref="T:System.Uri" /> instance.</summary>
    /// <returns>A <see cref="T:System.String" /> that contains the specified portion of the <see cref="T:System.Uri" /> instance.</returns>
    /// <param name="part">One of the <see cref="T:System.UriPartial" /> values that specifies the end of the URI portion to return. </param>
    /// <exception cref="T:System.InvalidOperationException">The current <see cref="T:System.Uri" /> instance is not an absolute instance. </exception>
    /// <exception cref="T:System.ArgumentException">The specified <paramref name="part" /> is not valid. </exception>
    TRefCountedPtr<String> GetLeftPart(UriPartial::Enum part);

    /// <summary>Converts a specified character into its hexadecimal equivalent.</summary>
    /// <returns>The hexadecimal representation of the specified character.</returns>
    /// <param name="character">The character to convert to hexadecimal representation. </param>
    /// <exception cref="T:System.ArgumentOutOfRangeException">
    static TRefCountedPtr<String> HexEscape(WCHAR character);

    /// <summary>Converts a specified hexadecimal representation of a character to the character.</summary>
    /// <returns>The character represented by the hexadecimal encoding at position <paramref name="index" />. If the character at <paramref name="index" /> is not hexadecimal encoded, the character at <paramref name="index" /> is returned. The value of <paramref name="index" /> is incremented to point to the character following the one returned.</returns>
    /// <param name="pattern">The hexadecimal representation of a character. </param>
    /// <param name="index">The location in <paramref name="pattern" /> where the hexadecimal representation of a character begins. </param>
    /// <exception cref="T:System.ArgumentOutOfRangeException">
    /// <paramref name="index" /> is less than 0 or greater than or equal to the number of characters in <paramref name="pattern" />. </exception>
    static WCHAR HexUnescape(String* pPattern, int* pIndex);

    /// <summary>Determines whether the current <see cref="T:System.Uri" /> instance is a base of the specified <see cref="T:System.Uri" /> instance.</summary>
    /// <returns>true if the current <see cref="T:System.Uri" /> instance is a base of <paramref name="uri" />; otherwise, false.</returns>
    /// <param name="uri">The specified <see cref="T:System.Uri" /> instance to test. </param>
    bool IsBaseOf(Uri* pUri);

    /// <summary>Determines whether a specified character is a valid hexadecimal digit.</summary>
    /// <returns>A <see cref="T:System.Boolean" /> value that is true if the character is a valid hexadecimal digit; otherwise false.</returns>
    /// <param name="character">The character to validate. </param>
    static bool IsHexDigit(WCHAR character);

    /// <summary>Determines whether a character in a string is hexadecimal encoded.</summary>
    /// <returns>A <see cref="T:System.Boolean" /> value that is true if <paramref name="pattern" /> is hexadecimal encoded at the specified location; otherwise, false.</returns>
    /// <param name="pattern">The string to check. </param>
    /// <param name="index">The location in <paramref name="pattern" /> to check for hexadecimal encoding. </param>
    bool IsHexEncoding(String* pPattern, int index);

    /// <summary>Indicates whether the string used to construct this <see cref="T:System.Uri" /> was well-formed and is not required to be further escaped.</summary>
    /// <returns>A <see cref="T:System.Boolean" /> value that is true if the string was well-formed in accordance with RFC 2396 and RFC 2732; else false. </returns>
    bool IsWellFormedOriginalString();

    /// <summary>Indicates whether the string is well-formed by attempting to construct a URI with the string and ensures that the string does not require further escaping.</summary>
    /// <returns>A <see cref="T:System.Boolean" /> value that is true if the string was well-formed in accordance with RFC 2396 and RFC 2732; else false.</returns>
    /// <param name="uriString">A URI.</param>
    /// <param name="uriKind">The type of the URI in <paramref name="uriString" />.</param>
    static bool IsWellFormedUriString(String* pUriString, UriKind::Enum uriKind);

    /// <summary>Determines the difference between two <see cref="T:System.Uri" /> instances.</summary>
    /// <returns>If the hostname and scheme of this URI instance and <paramref name="toUri" /> are the same, then this method returns a relative <see cref="T:System.Uri" /> that, when appended to the current URI instance, yields <paramref name="toUri" />.If the hostname or scheme is different, then this method returns a <see cref="T:System.Uri" />  that represents the <paramref name="toUri" /> parameter.</returns>
    /// <param name="uri">The URI to compare to the current URI.</param>
    /// <exception cref="T:System.InvalidOperationException">This instance represents a relative URI, and this property is valid only for absolute URIs. </exception>
    TRefCountedPtr<Uri> MakeRelativeUri(Uri* pUri);

    /// <summary>Creates a new <see cref="T:System.Uri" /> using the specified <see cref="T:System.String" /> instance and a <see cref="T:System.UriKind" />.</summary>
    /// <returns>A <see cref="T:System.Boolean" /> value that is true if the <see cref="T:System.Uri" /> was successfully created; otherwise, false.</returns>
    /// <param name="uriString">The <see cref="T:System.String" /> representing the <see cref="T:System.Uri" />.</param>
    /// <param name="uriKind">The type of the Uri.</param>
    /// <param name="result">When this method returns, contains the constructed <see cref="T:System.Uri" />.</param>
    static bool TryCreate(String* pUriString, UriKind::Enum uriKind, TRefCountedPtr<Uri>* ppResult);

    /// <summary>Creates a new <see cref="T:System.Uri" /> using the specified base and relative <see cref="T:System.String" /> instances.</summary>
    /// <returns>A <see cref="T:System.Boolean" /> value that is true if the <see cref="T:System.Uri" /> was successfully created; otherwise, false.</returns>
    /// <param name="baseUri">The base <see cref="T:System.Uri" />.</param>
    /// <param name="relativeUri">The relative <see cref="T:System.Uri" />, represented as a <see cref="T:System.String" />, to add to the base <see cref="T:System.Uri" />.</param>
    /// <param name="result">When this method returns, contains a <see cref="T:System.Uri" /> constructed from <paramref name="baseUri" /> and <paramref name="relativeUri" />. This parameter is passed uninitialized.</param>
    static bool TryCreate(Uri* pBaseUri, String* pRelativeUri, TRefCountedPtr<Uri>* ppResult);

    /// <summary>Creates a new <see cref="T:System.Uri" /> using the specified base and relative <see cref="T:System.Uri" /> instances.</summary>
    /// <returns>A <see cref="T:System.Boolean" /> value that is true if the <see cref="T:System.Uri" /> was successfully created; otherwise, false.</returns>
    /// <param name="baseUri">The base <see cref="T:System.Uri" />. </param>
    /// <param name="relativeUri">The relative <see cref="T:System.Uri" /> to add to the base <see cref="T:System.Uri" />. </param>
    /// <param name="result">When this method returns, contains a <see cref="T:System.Uri" /> constructed from <paramref name="baseUri" /> and <paramref name="relativeUri" />. This parameter is passed uninitialized.</param>
    static bool TryCreate(Uri* pBaseUri, Uri* pRelativeUri, TRefCountedPtr<Uri>* ppResult);

    /// <summary>Converts a string to its unescaped representation.</summary>
    /// <returns>A <see cref="T:System.String" /> that contains the unescaped representation of <paramref name="stringToUnescape" />. </returns>
    /// <param name="stringToUnescape">The string to unescape.</param>
    /// <exception cref="T:System.ArgumentNullException">
    /// <paramref name="stringToUnescape" /> is null. </exception>
    static TRefCountedPtr<String> UnescapeDataString(String* pStringToUnescape);

#pragma endregion Public Methods

#pragma region Public Properties

    /// <summary>Gets the absolute path of the URI.</summary>
    /// <returns>A <see cref="T:System.String"></see> containing the absolute path to the resource.</returns>
    TRefCountedPtr<String> get_AbsolutePath();

    /// <summary>Gets the absolute URI.</summary>
    /// <returns>A <see cref="T:System.String" /> containing the entire URI.</returns>
    TRefCountedPtr<String> get_AbsoluteUri();

    /// <summary>Gets the Domain Name System (DNS) host name or IP address and the port number for a server.</summary>
    /// <returns>A <see cref="T:System.String" /> containing the authority component of the URI represented by this instance.</returns>
    TRefCountedPtr<String> get_Authority();

    /// <summary>Gets an unescaped host name that is safe to use for DNS resolution.</summary>
    /// <returns>A <see cref="T:System.String" /> that contains the unescaped host part of the URI that is suitable for DNS resolution; or the original unescaped host string, if it is already suitable for resolution.</returns>
    /// <exception cref="T:System.InvalidOperationException">This instance represents a relative URI, and this property is valid only for absolute URIs. </exception>
    TRefCountedPtr<String> get_DnsSafeHost();

    /// <summary>Gets the escaped URI fragment.</summary>
    /// <returns>A <see cref="T:System.String" /> that contains any URI fragment information.</returns>
    TRefCountedPtr<String> get_Fragment();

    /// <summary>Gets the host component of this instance.</summary>
    /// <returns>A <see cref="T:System.String" /> that contains the host name. This is usually the DNS host name or IP address of the server.</returns>
    TRefCountedPtr<String> get_Host();
 
    /// <summary>Gets the type of the host name specified in the URI.</summary>
    /// <returns>A member of the <see cref="T:System.UriHostNameType" /> enumeration.</returns>
    UriHostNameType::Enum get_HostNameType();

    /// <summary>Gets a <see cref="T:System.Boolean"></see> value that indicates whether the <see cref="T:System.Uri"></see> instance is absolute.</summary>
    /// <returns>true if the <see cref="T:System.Uri"></see> instance is absolute; otherwise, false.</returns>
    bool get_IsAbsoluteUri() { return m_pSyntax != NULL; }

    /// <summary>Gets whether the port value of the URI is the default for this scheme.</summary>
    /// <returns>A <see cref="T:System.Boolean" /> value that is true if the value in the <see cref="P:System.Uri.Port" /> property is the default port for this scheme; otherwise, false.</returns>
    bool get_IsDefaultPort();

    /// <summary>Gets a value indicating whether the specified <see cref="T:System.Uri" /> is a file URI.</summary>
    /// <returns>A <see cref="T:System.Boolean" /> value that is true if the <see cref="T:System.Uri" /> is a file URI; otherwise, false.</returns>
    bool get_IsFile();

    /// <summary>Gets whether the specified <see cref="T:System.Uri" /> references the local host.</summary>
    /// <returns>A <see cref="T:System.Boolean" /> value that is true if this <see cref="T:System.Uri" /> references the local host; otherwise, false.</returns>
    bool get_IsLoopback();

    /// <summary>Gets whether the specified <see cref="T:System.Uri" /> is a universal naming convention (UNC) path.</summary>
    /// <returns>A <see cref="T:System.Boolean" /> value that is true if the <see cref="T:System.Uri" /> is a UNC path; otherwise, false.</returns>
    /// <exception cref="T:System.InvalidOperationException">This instance represents a relative URI, and this property is valid only for absolute URIs. </exception>
    bool get_IsUnc();

    /// <summary>Gets a local operating-system representation of a file name.</summary>
    /// <returns>A <see cref="T:System.String" /> that contains the local operating-system representation of a file name.</returns>
    TRefCountedPtr<String> get_LocalPath();

    /// <summary>Gets the original URI string that was passed to the <see cref="T:System.Uri" /> constructor.</summary>
    /// <returns>A <see cref="T:System.String" /> containing the exact URI specified when this instance was constructed; otherwise, <see cref="F:System.String.Empty" />.</returns>
    TRefCountedPtr<String> get_OriginalString() { return m_pString; }

    /// <summary>Gets the <see cref="P:System.Uri.AbsolutePath" /> and <see cref="P:System.Uri.Query" /> properties separated by a question mark (?).</summary>
    /// <returns>A <see cref="T:System.String" /> that contains the <see cref="P:System.Uri.AbsolutePath" /> and <see cref="P:System.Uri.Query" /> properties separated by a question mark (?).</returns>
    TRefCountedPtr<String> get_PathAndQuery();

    /// <summary>Gets the port number of this URI.</summary>
    /// <returns>An <see cref="T:System.Int32" /> value that contains the port number for this URI.</returns>
    /// <exception cref="T:System.InvalidOperationException">This instance represents a relative URI, and this property is valid only for absolute URIs. </exception>
    int get_Port();

    /// <summary>Gets any query information included in the specified URI.</summary>
    /// <returns>A <see cref="T:System.String" /> that contains any query information included in the specified URI.</returns>
    TRefCountedPtr<String> get_Query();

    /// <summary>Gets the scheme name for this URI.</summary>
    /// <returns>A <see cref="T:System.String" /> that contains the scheme for this URI, converted to lowercase.</returns>
    /// <exception cref="T:System.InvalidOperationException">This instance represents a relative URI, and this property is valid only for absolute URIs. </exception>
    TRefCountedPtr<String> get_Scheme();

    /// <summary>Indicates that the URI string was completely escaped before the <see cref="T:System.Uri" /> instance was created.</summary>
    /// <returns>A <see cref="T:System.Boolean" /> value that is true if the <paramref name="dontEscape" /> parameter was set to true when the <see cref="T:System.Uri" /> instance was created; otherwise, false.</returns>
    bool get_UserEscaped() { return InFact(Flags::UserEscaped); }

    /// <summary>Gets the user name, password, or other user-specific information associated with the specified URI.</summary>
    /// <returns>A <see cref="T:System.String" /> that contains the user information associated with the URI. The returned value does not include the '@' character reserved for delimiting the user information part of the URI.</returns>
    TRefCountedPtr<String> get_UserInfo();

#pragma endregion Public Properties

public:

#pragma region Internal Methods
    // Minor deviation, moved from private c'tor to internal c'tor
    Uri(long long flags, UriParser* pUriParser, String* pUri) : m_Flags(flags), m_pSyntax(pUriParser), m_pString(pUri) { }

    static TRefCountedPtr<Uri> CreateHelper(String* pUriString, bool dontEscape, UriKind::Enum uriKind, TRefCountedPtr<UriFormatException>* ppE);
    TRefCountedPtr<String> GetComponentsHelper(UriComponents::Enum uriComponents, UriFormat::Enum uriFormat);
    TRefCountedPtr<String> GetParts(unsigned int uriParts, UriFormat::Enum formatAs) { return GetComponents((UriComponents::Enum)uriParts, formatAs); }
    bool InternalIsWellFormedOriginalString();
    bool IsBaseOfHelper(TRefCountedPtr<Uri> pUriLink);
    TRefCountedPtr<UriFormatException> ParseMinimal();
    void ParseRemaining();
    static TRefCountedPtr<Uri> ResolveHelper(Uri* pBaseUri, Uri* pRelativeUri, TRefCountedPtr<String>* ppNewUriString, bool* pUserEscaped, TRefCountedPtr<UriFormatException>* ppE);

#pragma endregion Internal Methods

#pragma region Internal Properties

    bool get_UserDrivenParsing() { return ((m_Flags & Flags::UserDrivenParsing) != Flags::Zero); }

#pragma endregion Internal Properties

private:

#pragma region Private Structures

    class Flags
    {
    public:
        const static long long AllUriInfoSet = 0x80000000L;
        const static long long AuthorityFound = 0x100000L;
        const static long long BackslashInPath = 0x8000L;
        const static long long BasicHostType = 0x50000L;
        const static long long CannotDisplayCanonical = 0x7fL;
        const static long long CanonicalDnsHost = 0x2000000L;
        const static long long DnsHostType = 0x30000L;
        const static long long DosPath = 0x8000000L;
        const static long long E_CannotDisplayCanonical = 0x1f80L;
        const static long long E_FragmentNotCanonical = 0x1000L;
        const static long long E_HostNotCanonical = 0x100L;
        const static long long E_PathNotCanonical = 0x400L;
        const static long long E_PortNotCanonical = 0x200L;
        const static long long E_QueryNotCanonical = 0x800L;
        const static long long E_UserNotCanonical = 0x80L;
        const static long long ErrorOrParsingRecursion = 0x4000000L;
        const static long long FirstSlashAbsent = 0x4000L;
        const static long long FragmentIriCanonical = 0x40000000000L;
        const static long long FragmentNotCanonical = 0x40L;
        const static long long HasUnicode = 0x200000000L;
        const static long long HasUserInfo = 0x200000L;
        const static long long HostNotCanonical = 4L;
        const static long long HostNotParsed = 0L;
        const static long long HostTypeMask = 0x70000L;
        const static long long HostUnicodeNormalized = 0x400000000L;
        const static long long IdnHost = 0x100000000L;
        const static long long ImplicitFile = 0x20000000L;
        const static long long IndexMask = 0xffffL;
        const static long long IntranetUri = 0x2000000000L;
        const static long long IPv4HostType = 0x20000L;
        const static long long IPv6HostType = 0x10000L;
        const static long long IriCanonical = 0x78000000000L;
        const static long long LoopbackHost = 0x400000L;
        const static long long MinimalUriInfoSet = 0x40000000L;
        const static long long NotDefaultPort = 0x800000L;
        const static long long PathIriCanonical = 0x10000000000L;
        const static long long PathNotCanonical = 0x10L;
        const static long long PortNotCanonical = 8L;
        const static long long QueryIriCanonical = 0x20000000000L;
        const static long long QueryNotCanonical = 0x20L;
        const static long long RestUnicodeNormalized = 0x800000000L;
        const static long long SchemeNotCanonical = 1L;
        const static long long ShouldBeCompressed = 0x2000L;
        const static long long UncHostType = 0x40000L;
        const static long long UncPath = 0x10000000L;
        const static long long UnicodeHost = 0x1000000000L;
        const static long long UnknownHostType = 0x70000L;
        const static long long UnusedHostType = 0x60000L;
        const static long long UseOrigUncdStrOffset = 0x4000000000L;
        const static long long UserDrivenParsing = 0x1000000L;
        const static long long UserEscaped = 0x80000L;
        const static long long UserIriCanonical = 0x8000000000L;
        const static long long UserNotCanonical = 2L;
        const static long long Zero = 0L;
    };

    class ParsingError
    {
    public:
        enum Enum
        {
            BadAuthority = 3,
            BadAuthorityTerminator = 11,
            BadFormat = 1,
            BadHostName = 8,
            BadPort = 10,
            BadScheme = 2,
            CannotCreateRelative = 12,
            EmptyUriString = 4,
            LastFatalErrIndex = 7,
            LastRelativeUriOkErrIndex = 4,
            MustRootedPath = 7,
            None = 0,
            NonEmptyHost = 9,
            SchemeLimit = 5,
            SizeLimit = 6
        };
    };

    class UnescapeMode
    {
    public:
        enum Enum
        {
            CopyOnly = 0,
            Escape = 1,
            EscapeUnescape = 3,
            Unescape = 2,
            UnescapeAll = 8,
            UnescapeAllOrThrow = 0x18,
            V1ToStringFlag = 4
        };
    };

    class Check
    {
    public:
        enum Enum
        {
            BackslashInPath = 0x10,
            DisplayCanonical = 2,
            DotSlashAttn = 4,
            DotSlashEscaped = 0x80,
            EscapedCanonical = 1,
            FoundNonAscii = 8,
            None = 0,
            NotIriCanonical = 0x40,
            ReservedFound = 0x20
        };
    };

    class MoreInfo;

    struct Offset
    {
    public:
        Offset() : Scheme(0), User(0), Host(0), PortValue(0), Path(0), Query(0), Fragment(0), End(0) { }
        unsigned short Scheme;
        unsigned short User;
        unsigned short Host;
        unsigned short PortValue;
        unsigned short Path;
        unsigned short Query;
        unsigned short Fragment;
        unsigned short End;
    };

    class UriInfo : public Object
    {
    public:
        TRefCountedPtr<String> DnsSafeHost;
        TRefCountedPtr<String> Host;
        TRefCountedPtr<Uri::MoreInfo> MoreInfo;
        Uri::Offset Offset;
        TRefCountedPtr<String> ScopeId;
        TRefCountedPtr<String> String;
    };

    class MoreInfo : public Object
    {
    public:
        TRefCountedPtr<String> AbsoluteUri;
        TRefCountedPtr<String> Fragment;
        int Hash;
        TRefCountedPtr<String> Path;
        TRefCountedPtr<String> Query;
        TRefCountedPtr<String> RemoteUrl;
    };

#pragma endregion Private Structures

#pragma region Private Methods

    unsigned short CheckAuthorityHelper(const WCHAR* pwch, unsigned short idx, unsigned short length, ParsingError::Enum* pErr, long long* pFlags, UriParser* pSyntax, TRefCountedPtr<String>* ppNewHost);
    Check::Enum CheckCanonical(const WCHAR* str, unsigned short* pIdx, unsigned short end, WCHAR delim);
    static bool CheckKnownSchemes(long long* lptr, unsigned short nChars, TRefCountedPtr<UriParser>* ppSyntax);
    static ParsingError::Enum CheckSchemeSyntax(WCHAR* ptr, unsigned short length, TRefCountedPtr<UriParser>* ppSyntax);
    static TRefCountedPtr<String> CombineUri(Uri* pBasePart, String* pRelativePart, UriFormat::Enum uriFormat);
    static TArrayPtr<WCHAR> Compress(TArrayPtr<WCHAR> dest, unsigned short start, int* pDestLength, UriParser* pSyntax);
    void CreateHostString();
    static TRefCountedPtr<String> CreateHostStringHelper(String* pStr, unsigned short idx, unsigned short end, long long* pFlags, TRefCountedPtr<String>* ppScopeId);
    void CreateThis(String* pUri, bool dontEscape, UriKind::Enum uriKind);
    void CreateThisFromUri(Uri* pOtherUri);
    void CreateUri(Uri* pBaseUri, String* pRelativeUri, bool dontEscape);
    void CreateUriInfo(long long cF);
    static TArrayPtr<WCHAR> EnsureDestinationSize(const WCHAR* pStr, TArrayPtr<WCHAR> dest, int currentInputPos, short charsToAdd, short minReallocateChars, int* pDestPos, int prevInputPos);
    void EnsureHostString(bool allowDnsOptimization);
    void EnsureParseRemaining() { if( (m_Flags & Flags::AllUriInfoSet) == Flags::Zero) ParseRemaining(); }
    TRefCountedPtr<UriInfo> EnsureUriInfo();
    static WCHAR EscapedAscii(WCHAR digit, WCHAR next);
    static void EscapeAsciiChar(WCHAR ch, WCHAR* to, int* pPos);
    static TArrayPtr<WCHAR> EscapeString(String* pInput, int start, int end, TArrayPtr<WCHAR> dest, int* pDestPos, bool isUriString, WCHAR force1, WCHAR force2, WCHAR rsvd);
    TArrayPtr<WCHAR> GetCanonicalPath(TArrayPtr<WCHAR> dest, int* pPos, UriFormat::Enum formatAs);
    static ParsingError::Enum GetCombinedString(Uri* pBaseUri, TRefCountedPtr<String> pRelativeStr, bool dontEscape, TRefCountedPtr<String>* ppResult);
    TRefCountedPtr<String> GetEscapedParts(UriComponents::Enum uriParts);
    static TRefCountedPtr<UriFormatException> GetException(ParsingError::Enum err);
    void GetHostViaCustomSyntax();
    TRefCountedPtr<String> GetLocalPath();
    TRefCountedPtr<String> GetRelativeSerializationString(UriFormat::Enum format);
    TRefCountedPtr<String> GetUnescapedParts(UriComponents::Enum uriParts, UriFormat::Enum formatAs);
    TRefCountedPtr<String> GetUriPartsFromUserString(UriComponents::Enum uriParts);

    void InitializeUri(ParsingError::Enum err, UriKind::Enum uriKind, TRefCountedPtr<UriFormatException>* ppE);
    bool InFact(long long flags) { return ((m_Flags & flags) != Flags::Zero); }

    static bool IsAsciiLetter(WCHAR character) { return (((character >= 'a') && (character <= 'z')) || ((character >= 'A') && (character <= 'Z'))); }
    static bool IsAsciiLetterOrDigit(WCHAR character) { return (IsAsciiLetter(character) || ((character >= '0') && (character <= '9'))); }
    static bool IsLWS(WCHAR ch);
    static bool IsNotReservedNotUnreservedNotHash(WCHAR c);
    static bool IsNotSafeForUnescape(WCHAR ch);
    static bool IsNotUnreserved(WCHAR c);

    bool NotAny(long long flags) { return ((m_Flags & flags) == Flags::Zero); }
 

    static ParsingError::Enum ParseScheme(String* pUriString, long long* pFlags, TRefCountedPtr<UriParser>* ppSyntax);
    static unsigned short ParseSchemeCheckImplicitFile(const WCHAR* rgUriString, unsigned short length, ParsingError::Enum* pErr, long long* pFlags, TRefCountedPtr<UriParser>* ppSyntax);
    static TRefCountedPtr<String> PathDifference(String* pPath1, String* pPath2, bool compareCase);
    ParsingError::Enum Uri::PrivateParseMinimal();

    TRefCountedPtr<String> ReCreateParts(UriComponents::Enum parts, unsigned short nonCanonical, UriFormat::Enum formatAs);
    static bool TestForSubPath(const WCHAR* pMe, unsigned short meLength, const WCHAR* pShe, unsigned short sheLength, bool ignoreCase);

 

    void SetUserDrivenParsing() { m_Flags = Flags::UserDrivenParsing | (m_Flags & Flags::UserEscaped); }
    static bool StaticInFact(long long allFlags, long long checkFlags) { return (allFlags & checkFlags) != Flags::Zero; }
    static bool StaticIsFile(UriParser* pSyntax);
    static bool StaticNotAny(long long allFlags, long long checkFlags) {  return ((allFlags & checkFlags) == Flags::Zero); }

    static void UnescapeOnly(WCHAR* pch, int start, int* pEnd, WCHAR ch1, WCHAR ch2, WCHAR ch3);
    static TArrayPtr<WCHAR> UnescapeString(const WCHAR* pStr, int start, int end, TArrayPtr<WCHAR> dest, int* pDestPosition, WCHAR rsvd1, WCHAR rsvd2, WCHAR rsvd3, UnescapeMode::Enum unescapeMode, 
                                           UriParser* pSyntax, bool isQuery, bool readOnlyConfig);

#pragma endregion Private Methods

#pragma region Private Properties

    long long get_HostType() { return (m_Flags & Flags::UnknownHostType); }
    bool get_IsDosPath() { return ((m_Flags & Flags::DosPath) != Flags::Zero); }
    bool get_IsImplicitFile() { return ((m_Flags & Flags::ImplicitFile) != Flags::Zero); }
    bool get_IsNotAbsoluteUri() { return m_pSyntax == NULL; }
    bool get_IsUncOrDosPath() { return ((m_Flags & Flags::UncPath | Flags::DosPath) != Flags::Zero); }
    bool get_IsUncPath() { return ((m_Flags & Flags::UncPath) != Flags::Zero); } 
    TRefCountedPtr<String> get_PrivateAbsolutePath();

    unsigned short get_SecuredPathIndex();

    UriParser* get_Syntax() { return m_pSyntax; }

#pragma endregion Private Properties

    TRefCountedPtr<String> m_pString;
    TRefCountedPtr<String> m_pDnsSafeHost;
    TRefCountedPtr<UriParser> m_pSyntax;
    TRefCountedPtr<UriInfo> m_pInfo;
    long long m_Flags;
};


class UriTypeConverter : public TypeConverter
{
public:
    virtual bool CanConvertFrom(Object* pContext, const Type& type) const;
    virtual ObjectPtr ConvertFrom(Object* pContext, Object* pObjectFrom) const;
    virtual bool CanConvertTo(Object* pContext, const Type& type) const { return false; };
    virtual ObjectPtr ConvertTo(Object* pContext, Object* pObjectFrom, const Type& type) const { LibraryCriticalError(); return NULL; }
};

};


